/*
 * Copyright (C) 2017 Ortega Froysa, Nicolás <nortega@themusicinnoise.net>
 * Author: Ortega Froysa, Nicolás <nortega@themusicinnoise.net>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * @file neocomm.h
 * @brief Include file for NeoComm
 */

#pragma once

#ifdef __cplusplus
extern "C" {
#include <ctime>
#include <cstdlib>
#else
#include <time.h>
#include <stdlib.h>
#endif

/**
 * @brief A structure with necessary variables for other users.
 */
struct NeoComm_user {
	char *nick; ///< The alias used by the user.
	char *hash; ///< The unique hash of the user.
};

/**
 * @brief Structure with relevant message information.
 */
struct NeoComm_message {
	char *msg; ///< The text message.
	time_t sent; ///< The time it was sent.
	struct NeoComm_user from; ///< Info on the peer who sent the message.
};

/**
 * @brief Structure with information on the status of the node's connection.
 */
struct NeoComm_stats {
	unsigned int good,
				 dubious,
				 cached,
				 incoming,
				 total;
};

/**
 * @brief Status of a sent message.
 */
enum {
	NC_PENDING  = 1, ///< Status of message is pending.
	NC_GOOD     = 2, ///< Message was sent properly.
	NC_BAD      = 3, ///< Message failed to send.
};

/**
 * @brief Initialize a local DHT node. If port is 0 then the default port
 * number will be used.
 *
 * @param port The port for the node to listen on.
 *
 * @return 1 upon success, 0 upon failure. Use NeoComm_get_last_error for a
 * text description of the error.
 */
int NeoComm_init(const unsigned short port);

/**
 * @brief Deinitialize the local node.
 */
void NeoComm_deinit();

/**
 * @brief Connect to a foreign node.
 *
 * @param address DNS/IP of the node.
 * @param port Port of the foreign node.
 *
 * @return 1 upon success, 0 upon failure. Use NeoComm_get_last_error for a
 * text description of the error.
 */
int NeoComm_connect(const char *address, const unsigned short port);

/**
 * @brief Import a list of nodes from a node file and connect to them.
 *
 * @param node_file Path to the node list file.
 *
 * @return 1 upon success, 0 upon failure. Use NeoComm_get_last_error for a
 * text description of the error.
 */
int NeoComm_import_nodes(const char *node_file);

/**
 * @brief Export current list of nodes into a file.
 *
 * @param node_file path to node list file.
 *
 * @return 1 upon success, 0 upon failure. Use NeoComm_get_last_error for a
 * text description of the error.
 */
int NeoComm_export_nodes(const char *node_file);

/**
 * @brief Retrieve statistics about the local node.
 *
 * @return A text description of the local node's status.
 */
struct NeoComm_stats NeoComm_get_node_stats();

/**
 * @brief Join a channel.
 *
 * @param channel_name Name of the channel.
 *
 * @return 1 upon success, 0 upon failure. Use NeoComm_get_last_error for a
 * text description of the error.
 */
int NeoComm_join_channel(const char *channel_name);

/**
 * @brief Leave a channel.
 *
 * @param channel_name Name of the channel to disconnect from.
 *
 * @return 1 upon success, 0 upon failure. Use NeoComm_get_last_error for a
 * text description of the error.
 */
int NeoComm_leave_channel(const char *channel_name);

/**
 * @brief Retrieve a list of the channels that the client is listening to.
 *
 * @return A list of channels separated by spaces.
 */
const char *NeoComm_get_channel_list();

/**
 * @brief Retrieve the current number of channels that the client is listening
 * to.
 *
 * @return The number of channels the client is listening to.
 */
unsigned int NeoComm_get_num_channels();

/**
 * @brief Get the next available message (in chronological order) for
 * a given channel.
 *
 * @param channel_name Channel to check for new messages.
 *
 * @return A structure of the message. If NULL then there are no new messages.
 *
 * @warning This command will remove the message from the internal list.
 * @warning The message must be freed from memory using NeoComm_free_message.
 */
struct NeoComm_message *NeoComm_get_next_message(const char *channel_name);

/**
 * @brief Free the memory for a message.
 *
 * @param msg The message to free from memory.
 */
void NeoComm_free_message(struct NeoComm_message *msg);

/**
 * @brief Send a message to a channel.
 *
 * @param channel_name Name of the channel to send the message to.
 * @param message A message to send to the channel.
 *
 * @return Upon success it returns the time that the message was sent which
 * can be used as a token to see if the message was sent properly using
 * NeoComm_check_message, if the function failed then 0 will be returned.
 */
time_t NeoComm_send_message(const char *channel_name, const char *message);

/**
 * @brief Check the status of a sent message.
 *
 * @param token The token of a message to check its status.
 *
 * @return The status of the message (NC_GOOD, NC_BAD, or NC_PENDING). If the
 * token does not exist then 0 is returned.
 *
 * @warning Once checked the token becomes invalid.
 */
int NeoComm_check_message(const time_t token);

/**
 * @brief Get the last error that occurred in text.
 *
 * @return A string with the last error occurred, if there are no errors it
 * return NULL.
 */
const char *NeoComm_get_last_error();

#ifdef __cplusplus
}
#endif
